////////////////////////////////////////////////
// DESCRIPTION:
//    Support for connected streams via a FIFO pair
//
// Legal Notices:
//   Copyright (c) 2008, Telliam Consulting, LLC.
//   All rights reserved.
//
//   Redistribution and use in source and binary forms, with or without modification,
//   are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice, this list
//     of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice, this
//     list of conditions and the following disclaimer in the documentation and/or other
//     materials provided with the distribution.
//   * Neither the name of Telliam Consulting nor the names of its contributors may be
//     used to endorse or promote products derived from this software without specific
//     prior written permission.
//
//   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
//   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
//   SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
//   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
//   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
//   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
//   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
//   DAMAGE. 
//
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;

namespace FusionWare.SPOT.EmulatorComponents
{
    /// <summary>Stream that uses a pair of FIFOs for independent read/write from different sources</summary>
    /// <remarks>
    /// It is sometimes useful to have a pair of streams refering to the same data channels
    /// in a sort of software NULL modem. (e.g. data written by one stream is read back out another 
    /// and vice versa) This class implements that pattern with a pair of FIFOs to allow independent
    /// reads and writes. 
    /// 
    /// In the current implementation any data written to the stream when the associated FIFO is full
    /// is simply ignored unless the FIFO is set to throw an exception when full. Future versions will
    /// support blocking reads and writes with timeouts. 
    /// </remarks>
    public class FifoStream : Stream
    {
        Fifo<byte> ReadFifo;
        Fifo<byte> WriteFifo;

        /// <summary>Creates a new FifoStream using the specified FIFOs</summary>
        /// <param name="ReadFifo">Fifo stream will read from</param>
        /// <param name="WriteFifo">FIFO stream will write to</param>
        /// <remarks>
        /// Generally applications create two instances of the <see cref="FifoStream"/>
        /// class with the constructor parameters reveresed for each. (e.g. the ReadFifo
        /// for one stream becomes the WriteFifo for the other and vice versa.) Thus the 2
        /// streams are "connected" via the FIFOs.
        /// </remarks>
        public FifoStream(Fifo<byte> ReadFifo, Fifo<byte> WriteFifo)
        {
            this.ReadFifo = ReadFifo;
            this.WriteFifo = WriteFifo;
        }

        /// <summary>Event fired whenever the stream receives data</summary>
        public event EventHandler DataReceived;

        /// <summary>Event fired when the stream is flushed</summary>
        public event EventHandler Flushed;

        #region Stream Overrides
        /// <summary>Flushes the stream</summary>
        /// <seealso cref="System.IO.Stream.Flush"/>
        public override void Flush()
        {
            this.ReadFifo.Flush();
            this.WriteFifo.Flush();
            if(this.Flushed != null)
                this.Flushed(this, null);
        }
        
        /// <summary>Reads data from the stream</summary>
        /// <param name="buffer">buffer to read the data into</param>
        /// <param name="offset">index into buffer to begin reading into</param>
        /// <param name="count">count of bytes to read from the stream</param>
        /// <returns>number of bytes actually read</returns>
        public override int Read(byte[] buffer, int offset, int count)
        {
            this.ReadFifo.Read(buffer, offset, count);
            return count;
        }

        /// <summary>Not Supported</summary>
        /// <param name="offset">not supported</param>
        /// <param name="origin">not supported</param>
        /// <returns>not supported</returns>
        /// <exception cref="NotSupportedException">Always thrown</exception>
        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        /// <summary>Not supported</summary>
        /// <param name="value">Not Supported</param>
        /// <exception cref="NotSupportedException">Always thrown</exception>
        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        /// <summary>Writes an array of Bytes to the stream</summary>
        /// <param name="buffer">buffer containing data to write</param>
        /// <param name="offset">offset in buffer of the first byte to write</param>
        /// <param name="count">count of bytes to write</param>
        /// <remarks>
        /// Writes the data to the stream and fires the <see cref="DataReceived"/> event
        /// </remarks>
        public override void Write(byte[] buffer, int offset, int count)
        {
            this.WriteFifo.Write(buffer, offset, count);
            if(this.DataReceived != null)
                this.DataReceived(this, null);
        }

        /// <summary>Number of bytes available to read</summary>
        public int AvailableBytes
        {
            get
            {
                return this.ReadFifo.Count;
            }
        }

        /// <summary>Indicates this tream can be read</summary>
        public override bool CanRead
        {
            get
            {
                return true;
            }
        }

        /// <summary>Indicates this stream does not support seek</summary>
        public override bool CanSeek
        {
            get
            {
                return false;
            }
        }

        /// <summary>indicates this stream is writable</summary>
        public override bool CanWrite
        {
            get
            {
                return true;
            }
        }

        /// <summary>Not supported</summary>
        /// <exception cref="NotSupportedException">Always thrown</exception>
        public override long Length
        {
            get
            {
                throw new NotSupportedException();
            }
        }

        /// <summary>Not supported</summary>
        /// <exception cref="NotSupportedException">Always thrown</exception>
        public override long Position
        {
            get
            {
                throw new NotSupportedException();
            }
            set
            {
                throw new NotSupportedException();
            }
        }
        #endregion
    }
}
